package scalab.caches.ccp

import scalab.utils.{HSQLDB, Reporter}
import scalab.core.{Cache, CacheCorePolicy}
import scalab.core.dependencies.{Dependency, SoftDependency, HardDependency, InterDependency}

/**
* Defines a conservative policy that takes files inter-dependencies into account.
*/

trait AccurateConservativeCCP extends CacheCorePolicy {

	/*todo: 
				-load / store (filter), debug 
				-optimize taskdirectory, tmpdir...
				-detect change serialVersionUID (due to update of scalab core) => invalidate
	*/

	import scalab.core.component.{Task, Filter, ExecutableComponent}

	import java.io.File
	import scala.collection.mutable.{Map, HashMap}//change to immutable?
	import scala.collection.immutable.{Set, HashSet}
	import scalab.utils.FileUtils

	import scalab.core.interpreter.Build
	import scalab.core.event._
	import scalab.utils.Path
	import java.sql.{SQLException, ResultSet}

	//creates required tables
	listenBuildStarted{e => AccurateConservativeCCP.initialize(cacheDirectory)}
	//commits db modifications from memory to persistent storage
	listenBuildFinished{e => AccurateConservativeCCP.disconnect}
	//override def initializeCCP = AccurateConservativeCCP.initialize(cacheDirectory)
	

	//-------------LOAD----------------

	//implement cache for filters
	def load(filter: Filter): Boolean = true
	def store(filter: Filter): Boolean = true


	def reset(component: ExecutableComponent) = {
		component.interDependencies.clear  //empties the relations of the task (cycles in the graph)
		commit //empties tmp table
		storedProducts = HashSet.empty[Dependency]
	}

	//..........

	var storedProducts: Set[Dependency] = _
	var runDir: String = _

	def productDirectory(task: Task): String = { //take inputs into account!!!!
		taskDirectory(task) + dependenciesHash(task.outOfDateSources) + File.separator
	}

	def tmpDir(task: Task): String = 
		taskDirectory(task) + "tmp" + File.separator

	def tmpDir(taskDir: String): String = 
		taskDir + "tmp" + File.separator

	def getAllRunProducts(task: Task, sources: Iterable[Dependency]): Option[(Set[InterDependency], Set[InterDependency])] = {
		//task.warning("----------LOAD-------------")
		reset(task)
		//task.succes("1 > reseted Relations")
		//info("dumping tasks 0:")
		//dumpTasks
		//storedProducts = //throw away!!!
		insertTmpDeps(sources)//take inputs into account!!
		val rs = selectRelations(task)//search for productRelations
		if(rs.next){

			var retrievedProducts: Set[InterDependency] = HashSet.empty[InterDependency]
			//sources that have been retrieved from the cache
			var upToDateSources: Set[Dependency] = HashSet.empty[Dependency]
			do{
				val productBytes = rs.getBytes("product")
				if(productBytes != null){//cache hit
					val dep = Cache.deserialize[Dependency](rs.getBytes("dependency"))
					//info("retrived dep: " + dep)
					upToDateSources = upToDateSources + dep//Cache.deserialize[Dependency](dep)
					retrievedProducts = retrievedProducts + Cache.deserialize[InterDependency](productBytes)
				}
			}while(rs.next) 
			//invalidate out-of-date sources
			val outOfDateSources = task.sourceSet -- upToDateSources
			task.invalidate(outOfDateSources)

			//compute corresponding out-of-date products (which will be rebuilt)
			//a product is out of date if one of the source it requires is out-of-date
			//or the product itself does not exists (it has been deleted)
			val outOfDateProducts = 
				(retrievedProducts filter {p => !p.dependency.exists || p.requireAny(outOfDateSources)})

			val upToDateProducts = HashSet.empty[InterDependency] ++ (retrievedProducts -- outOfDateProducts)
			Some((upToDateProducts, outOfDateProducts))
		}
		else
			None
	}

	def getRunProducts(task: Task, sources: Iterable[Dependency]): (Boolean, Option[Set[Dependency]]) = {
		val prods = getAllRunProducts(task, sources)
		if(prods.isEmpty){
			(false, None)
		}
		else{
			(
				!(task.inputs exists {i => !i.outOfDateSources.isEmpty}), 
				Some(prods.get._1 map {r => r.dependency})
			)
		}
	}

	def load(task: Task): Boolean = {

		val prods = getAllRunProducts(task, task.sources)
		if(prods.isEmpty)task.invalidateSources
		else{
			val (upToDateProducts, outOfDateProducts) = prods.get

			//invalidate out-of-date products (and every sources they require)
			task.invalidateProducts(outOfDateProducts)

			//if task will execute, we can tidy up the tmp directory
			//this is not the case if all dependencies are retrived 
			//from cache because recurrent products would not be there
			if(task.inputs.exists({i => !i.outOfDateSources.isEmpty})){
				//warning("tidying up tmp dir!")
				FileUtils.emptyDir(tmpDir(task))
			}

			//copy up-to-date-products in tmp => following component has sound product directory
			//todo: check whether products have been successfully copied
			//if not => invalidate those products to rebuild them
			val upToDateProdsLst = upToDateProducts.toList map {p => p.dependency}
			storedProducts = storedProducts ++  
				FileUtils.copyDependencies(upToDateProdsLst, 
					upToDateProdsLst map {d => 
						tmpDir(task) + Path.subPath(Path.removePrefix(d.canonicalPath, taskDirectory(task)))
					}
				)
		}
			
		//task.succes("2 > loaded Relations")
		
		//execute task if any input contains an out-of-date dependency
		if(task.inputs.exists({i => !i.outOfDateSources.isEmpty})){
			runDir = productDirectory(task) //computing product directory with respect to out-of-date deps
			//info("runDir: " + runDir)
			setTaskProductDir(task,tmpDir(task))
			//task.info("3 > product directory: " + task.productDir)
			true // execute task!
		}
		else{
			storedProducts = storedProducts ++ getRecurrentProducts(task)
			task transmit storedProducts
			//task.info("Retrieved all dependencies from cache: " + storedProducts.mkString("\n","\n",""))
			//task.info("Cache size is " + cacheSize)
			false//no out of date deps
		}
	}

	def getRecurrentProducts(task: Task): Set[Dependency] = {
		val rs = selectRecurrentProducts(task)
		if(rs.next)
			Cache.deserialize[Set[Dependency]](rs.getBytes("dependencies"))
		else
			HashSet.empty[Dependency]
	}

	def getProductInterDependency(task: Task): Map[Dependency, Option[InterDependency]] = {
		val list = task.productDependencies.toList
		new HashMap[Dependency, Option[InterDependency]] ++ (list zip task.interDependencies.getAllOptions(list))
	}

	def store(task: Task): Boolean = {
	
		val taskDir = taskDirectory(task)
		var recurrentProducts: Set[Dependency] = HashSet.empty[Dependency]
		val dependenciesProducts: Map[Dependency, Set[InterDependency]] = HashMap.empty[Dependency, Set[InterDependency]]
		val productsHash: Map[InterDependency, String] = HashMap.empty[InterDependency, String]
		
		getProductInterDependency(task).elements foreach {e => //add productRelations in cache...
			if(e._2.isEmpty){//recurrent product
				recurrentProducts = recurrentProducts + e._1
			}
			else {
				val rel = e._2.get
				//backups dependency from tmp dir to run's directory
				//info("copying " + rel.dependency.canonicalPath + " from " + tmpDir(taskDir) + " to " + runDir)
				rel.dependency = FileUtils.copyDependency(rel.dependency, tmpDir(taskDir), runDir)
				productsHash.update(rel, dependencyHash(rel.dependency))
				rel.allChildrenDependencies foreach {d => 
					dependenciesProducts.get(d) match{
						case None =>
							dependenciesProducts.update(d, HashSet.empty[InterDependency] + rel)
						case Some(s) =>
							dependenciesProducts.update(d, s + rel)
					}
				}
			}
		}

		
		val productsIds = insertProducts(productsHash, insertProductDir(runDir))
		var task_db_id: Int = insertTask(task)
		insertDependenciesProducts(task_db_id, productsIds, dependenciesProducts)
		//make batch!!!!
		//dependenciesProducts.elements foreach{p =>
		//	task_db_id = insertRelation(task, p._1, p._2)
		//}

		//may happen only if there are only recurrentProducts
		insertRecurrentProducts(task_db_id, recurrentProducts)

		//commit stored work (so that if a cycle redirects to this task, db is sound)
		commit 

		//transmit products loaded from cache...
		//info("storedProducts in store: " + storedProducts.mkString("\n","\n",""))
		task transmit storedProducts 
		true
	}

	def getLongInsertId: long = {
		val rs = AccurateConservativeCCP.getIdStatement.executeQuery
		rs.next
		//val rs = AccurateConservativeCCP.getIdStatement.getResultSet
		rs.getLong(1)
	}

	def getIntInsertId: Int = {
		val rs = AccurateConservativeCCP.getIdStatement.executeQuery
		rs.next
		//info("insert 1: " +AccurateConservativeCCP.getIdStatement.isClosed)
		//flush
		// = AccurateConservativeCCP.getIdStatement.getResultSet
		rs.getInt(1)
	}

	def retrieveTaskId(task: Task): Int = {
		AccurateConservativeCCP.retrieveTaskIdStatement.setString(1, task.id)
		val rs = AccurateConservativeCCP.retrieveTaskIdStatement.executeQuery
		rs.next
		rs.getInt(1)
	}

	def retrieveProductDirId(dir: String): Int = {
		AccurateConservativeCCP.retrieveProdDirIdStatement.setString(1, dir)
		val rs = AccurateConservativeCCP.retrieveProdDirIdStatement.executeQuery
		rs.next
		rs.getInt(1)
	}

	def selectRelations(component: ExecutableComponent): ResultSet = {
		AccurateConservativeCCP.selectStatement.setString(1, component.id)
		AccurateConservativeCCP.selectStatement.executeQuery
	}

	def selectRecurrentProducts(task: Task): ResultSet = {
		AccurateConservativeCCP.recurrentSelectStatement.setInt(1, retrieveTaskId(task))
		AccurateConservativeCCP.recurrentSelectStatement.executeQuery
	}

	def insertTask(task: Task): Int = try{
		AccurateConservativeCCP.insertTaskStatement.setString(1, task.id)
		AccurateConservativeCCP.insertTaskStatement.executeUpdate
		getIntInsertId
	}
	catch{
		case se: SQLException if(se.getErrorCode == HSQLDB.UNIQUE_CONSTRAINT_VIOLATED) => 
			retrieveTaskId(task)//unique constraint violated => retrieve id
	}

	def insertDependency(dep: Dependency): long = try{
		AccurateConservativeCCP.insertDependencyStatement.setString(1, dependencyHash(dep))
		AccurateConservativeCCP.insertDependencyStatement.setBytes(2, Cache.serialize(dep))
		AccurateConservativeCCP.insertDependencyStatement.executeUpdate
		getLongInsertId
	}
	catch{
		case se: SQLException if(se.getErrorCode == HSQLDB.UNIQUE_CONSTRAINT_VIOLATED) => 
			retrieveDepId(dep)
	}

	def retrieveDepId(dep: Dependency): long = {
		AccurateConservativeCCP.retrieveDepIdStatement.setString(1, dependencyHash(dep))
		val rs = AccurateConservativeCCP.retrieveDepIdStatement.executeQuery
		rs.next
		rs.getLong(1)
	}

	def insertProductDir(dir: String): Int = try{
		AccurateConservativeCCP.insertProductDirStatement.setString(1, dir)
		AccurateConservativeCCP.insertProductDirStatement.executeUpdate
		getIntInsertId
	}
	catch{
		case se: SQLException  if(se.getErrorCode == HSQLDB.UNIQUE_CONSTRAINT_VIOLATED) => 
			retrieveProductDirId(dir)
	}

	def insertDependencies(deps: Iterable[Dependency]){
		deps foreach {d =>
			AccurateConservativeCCP.insertDependencyStatement.setString(1,dependencyHash(d))
			AccurateConservativeCCP.insertDependencyStatement.setBytes(2, Cache.serialize(d))
			AccurateConservativeCCP.insertDependencyStatement.addBatch
		}
		AccurateConservativeCCP.insertDependencyStatement.executeBatch
	}

	def insertTmpDeps(deps: Iterable[Dependency]){
		//warning("insertTmpStatement: " + insertTmpDepStatement)
		deps foreach {d =>
			AccurateConservativeCCP.insertTmpDepStatement.setString(1,dependencyHash(d))
			AccurateConservativeCCP.insertTmpDepStatement.addBatch
		}
		AccurateConservativeCCP.insertTmpDepStatement.executeBatch
	}

	def insertProducts(products: Map[InterDependency, String], dir_id: Int): Map[InterDependency, Long] = {
		products.elements foreach {p =>
			AccurateConservativeCCP.insertProductStatement.setString(1, p._2)
			AccurateConservativeCCP.insertProductStatement.setBytes(2, Cache.serialize(p._1))
			AccurateConservativeCCP.insertProductStatement.setInt(3, dir_id)
			AccurateConservativeCCP.insertProductStatement.addBatch
		}
		AccurateConservativeCCP.insertProductStatement.executeBatch
		retrieveProductsIds(products)
	}

	def insertDependenciesProducts(task_db_id: Int, 
																	productsIds: Map[InterDependency, Long], 
																	dependenciesRelations: Map[Dependency, Set[InterDependency]]
																){

		val dependencies = (productsIds.elements map {p => p._1.dependency}).toList
		dependenciesRelations foreach {dr =>
			val dep_id = insertDependency(dr._1)
			dr._2 foreach {pr =>
				var product_id: Long = -1
				val res = productsIds.elements find {p => p._1 == pr}
				if(!res.isEmpty){
					product_id = res.get._2
					
				}
				AccurateConservativeCCP.insertRelationStatement.setInt(1, task_db_id)
				AccurateConservativeCCP.insertRelationStatement.setLong(2, dep_id)
				AccurateConservativeCCP.insertRelationStatement.setLong(3, product_id)
				AccurateConservativeCCP.insertRelationStatement.addBatch
			}
		}
		
		AccurateConservativeCCP.insertRelationStatement.executeBatch
	}


	def retrieveProductsIds(products: Map[InterDependency, String]): Map[InterDependency, Long] = {
		products.elements foreach {p =>
			AccurateConservativeCCP.insertTmpProductStatement.setString(1, p._2)
			AccurateConservativeCCP.insertTmpProductStatement.addBatch
		}
		AccurateConservativeCCP.insertTmpProductStatement.executeBatch
		val rs = AccurateConservativeCCP.retrieveProductsIdsStatement.executeQuery
		val map: Map[InterDependency, Long] = Map.empty[InterDependency, Long]
		while(rs.next){
			map.update(Cache.deserialize[InterDependency](rs.getBytes("product")), rs.getLong("id"))
		}
		map
	}

	def insertRecurrentProducts(task_db_id: Int, recurrentProducts: Set[Dependency]) = try{
		AccurateConservativeCCP.insertRecProdsStatement.setInt(1, task_db_id)
		AccurateConservativeCCP.insertRecProdsStatement.setBytes(2, Cache.serialize(recurrentProducts))
		AccurateConservativeCCP.insertRecProdsStatement.executeUpdate
	}
	catch{
		//UNIQUE constraint violated (recurrent products are already in database)
		case se: SQLException if(se.getErrorCode == HSQLDB.UNIQUE_CONSTRAINT_VIOLATED) => 
	}

	def commit = AccurateConservativeCCP.connection.commit

	def dumpTasks{
		val rs = AccurateConservativeCCP.debugStatement.executeQuery("SELECT id, hash FROM tasks")
		while(rs.next){
			info("task" + rs.getInt(1) + " : " + rs.getString(2))
		}
	}


	/**
	* Returns a lower approximation of freed space
	* That is, database size is not taken into account
	*	because database must be written to disc in order 
	*	to estimate its size which is inefficient.
	*/
	def free(runDir: File): Long = {
		if(runDir.exists){
			info("deleting dir: " + runDir.getCanonicalPath)
			val productsSize = FileUtils.dirSize(runDir)
			AccurateConservativeCCP.deleteProductDirStatement.setString(1, runDir.getCanonicalPath + File.separator)
			AccurateConservativeCCP.deleteProductDirStatement.executeUpdate
			commit
			FileUtils.rmDir(runDir)
			info("Evicted Cache size is " + cacheSize)
			productsSize
		}
		else 0L
	}

	def getCachedProductDirs(taskDir: File): Iterable[File] = {
		val rs = AccurateConservativeCCP.selectProductDirsStatement.executeQuery
		var dirs: List[File] = Nil
		while(rs.next){
			dirs = (new File(rs.getString("dir_path"))) :: dirs
		}
		dirs
	}
}

object AccurateConservativeCCP extends HSQLDB with Reporter{

	import java.sql._


	val name: String = "ACCCP"
	//prepared insert statements
	var insertRelationStatement     : PreparedStatement = _
	var insertTaskStatement         : PreparedStatement = _
	var insertDependencyStatement   : PreparedStatement = _
	var insertTmpDepStatement       : PreparedStatement = _
	var insertRecProdsStatement     : PreparedStatement = _
	var insertProductStatement      : PreparedStatement = _
	var insertTmpProductStatement   : PreparedStatement = _
	var insertProductDirStatement      : PreparedStatement = _

	//prepare select statements
	var getIdStatement              : PreparedStatement = _
	var selectStatement             : PreparedStatement = _
	var retrieveTaskIdStatement     : PreparedStatement = _
	var retrieveDepIdStatement      : PreparedStatement = _
	var retrieveProductsIdsStatement: PreparedStatement = _
	var retrieveProdDirIdStatement  : PreparedStatement = _
	var selectProductDirsStatement  : PreparedStatement = _
	
	//prepare delete statements
	var deleteProductDirStatement   : PreparedStatement = _
	
	var recurrentSelectStatement    : PreparedStatement = _
		

	var debugStatement: Statement = _

	def initialize(cacheDir: String): Boolean = { //mechanism for controling cache database uniqueness!
		//set autocommit to false for performances
		//and control tmp tables (tmp table are emptied on commit)
		if(!connect(cacheDir + databaseName, false)){ //cache was not initialized
			error("Failed to connect to HSQL Database!")
			false
		}
		else try{
			//create tables
			createTasksTable
			createDependenciesTable
			createProductDirsTable
			createProductsTable
			createRelationsTable
			createRecurrentProductsTable
			
			createTmpDepsTable
			createTmpProductsTable

			//prepare statements
			prepareInsertRelation
			prepareInsertTask
			prepareInsertDependency
			prepareInsertTmpDep
			prepareInsertProductDir
			prepareSelect
			prepareGetId
			prepareRetrieveTaskId
			prepareRetrieveDepId
			prepareInsertRecurrentProducts
			prepareRecurrentSelect
			prepareInsertProduct
			prepareInsertTmpProduct
			prepareRetrieveProductsIds
			prepareRetrieveProdDirId
			prepareSelectProductDirs

			prepareDeleteProductDir
			debugStatement = connection.createStatement
			true
		}
		catch{
			case se: SQLException =>
				error("HSQL Error: " + se.getMessage)
				false
		}
	}

	val databaseName: String = "acccp"

	def createRelationsTable: Boolean = {
		val tableName = "dependenciesProducts"
		createTable(
			tableName,
			"CREATE MEMORY TABLE " + tableName + " (task_id INTEGER, dep_id BIGINT, product_id BIGINT," +
			"CONSTRAINT pk PRIMARY KEY(task_id, dep_id, product_id), " +
			"FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE, " +
			"FOREIGN KEY (dep_id) REFERENCES dependencies(id) ON DELETE CASCADE, " +
			"FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE)"
		)
	}

	def createProductsTable: Boolean = {
		val tableName = "products"
		createTable(
			tableName, 
			"CREATE MEMORY TABLE " + tableName + " (id BIGINT IDENTITY, hash VARCHAR(32), product BINARY, dir_id INTEGER, " +
			"FOREIGN KEY (dir_id) REFERENCES productDirs(id) ON DELETE CASCADE, " +
			"CONSTRAINT p_un UNIQUE(hash))"
		)
	}

	def createTasksTable: Boolean = {
		val tableName = "tasks"
		createTable(
			tableName, 
			"CREATE MEMORY TABLE " + tableName + " (id INTEGER IDENTITY, hash VARCHAR(32), CONSTRAINT t_un UNIQUE(hash))"
		)
	}

	def createDependenciesTable: Boolean = {
		val tableName = "dependencies"
		createTable(
			tableName, 
			"CREATE MEMORY TABLE " + tableName + " (id BIGINT IDENTITY, hash VARCHAR(32), dependency BINARY, CONSTRAINT d_un UNIQUE(hash))"
		)
	}

	def createTmpDepsTable: Boolean = {
		val tableName = "tmpDeps"
		createTable(
			tableName,
			"CREATE GLOBAL TEMPORARY TABLE " + tableName + " (hash VARCHAR(32)) ON COMMIT DELETE ROWS" //no index for faster insertion
			//no need for unique check as soon as dependencies are coming from a set
		)
	}

	def createTmpProductsTable: Boolean = {
		val tableName = "tmpProducts"
		createTable(
			tableName,
			"CREATE GLOBAL TEMPORARY TABLE " + tableName + " (hash VARCHAR(32)) ON COMMIT DELETE ROWS" //no index for faster insertion
			//no need for unique check as soon as products are coming from a map
		)
	}

	def createRecurrentProductsTable: Boolean = {
		val tableName = "recurrentProducts"
		createTable(
			tableName, 
			"CREATE MEMORY TABLE " + tableName + " (task_id INTEGER, dependencies LONGVARBINARY, " +
			"CONSTRAINT r_un UNIQUE(task_id), " +
			"FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE)"
		)
	}

	def createProductDirsTable: Boolean = {
		val tableName = "productDirs"
		createTable(
			tableName,
			"CREATE MEMORY TABLE " + tableName + "(id INTEGER IDENTITY, dir_path VARCHAR, " +
			"CONSTRAINT pd_un UNIQUE(dir_path))"
		)
	}

	def prepareInsertProduct{
		insertProductStatement = connection.prepareStatement(
			"INSERT INTO products(id, hash, product, dir_id) VALUES (NULL,?,?,?)"
		)
	}

	def prepareInsertProductDir{
		insertProductDirStatement = connection.prepareStatement(
			"INSERT INTO productDirs(id, dir_path) VALUES (NULL,?)"
		)
	}

	def prepareInsertTmpProduct{
		insertTmpProductStatement = connection.prepareStatement(
			"INSERT INTO tmpProducts(hash) VALUES (?)"
		)
	}

	def prepareRetrieveProductsIds{
		retrieveProductsIdsStatement = connection.prepareStatement(
			"SELECT P.product, P.id FROM products P INNER JOIN tmpProducts TMP ON P.hash=TMP.hash"
		)
	}

	def prepareInsertRelation{
		insertRelationStatement = connection.prepareStatement(
			"INSERT INTO dependenciesProducts(task_id,dep_id,product_id) VALUES (?,?,?)"
		)
	}

	def prepareInsertTask{
		insertTaskStatement = connection.prepareStatement(
			"INSERT INTO tasks(id, hash) VALUES (NULL,?)"
		)
	}

	def prepareInsertDependency{
		insertDependencyStatement = connection.prepareStatement(
			"INSERT INTO dependencies(id, hash, dependency) VALUES (NULL,?,?)"
		)
	}

	def prepareInsertTmpDep{
		insertTmpDepStatement = connection.prepareStatement(
			"INSERT INTO tmpDeps(hash) VALUES (?)"
		)
	}

	def prepareInsertRecurrentProducts{
		insertRecProdsStatement = connection.prepareStatement(
			"INSERT INTO recurrentProducts(task_id, dependencies) VALUES (?,?)"
		)
	}

	def prepareSelect{
		selectStatement = connection.prepareStatement(
			"SELECT TMP.hash, D.dependency, P.product FROM tmpDeps TMP " +
			"LEFT OUTER JOIN dependencies D ON TMP.hash=D.hash " +
			"LEFT OUTER JOIN dependenciesProducts DP ON DP.dep_id=D.id " +
			"LEFT OUTER JOIN tasks T ON DP.task_id=T.id " + 
			"LEFT OUTER JOIN Products P ON DP.product_id=P.id " +
			"WHERE T.hash=?"
		)
	}

	def prepareRecurrentSelect{
		recurrentSelectStatement = connection.prepareStatement(
			"SELECT dependencies FROM recurrentProducts WHERE task_id = ?"
		)
	}

	def prepareGetId{
		getIdStatement = connection.prepareStatement(
			"CALL IDENTITY()"
		)
	}

	def prepareRetrieveTaskId{
		retrieveTaskIdStatement = connection.prepareStatement(
			"SELECT id FROM tasks WHERE hash = ?"
		)
	}

	def prepareRetrieveProdDirId{
		retrieveProdDirIdStatement = connection.prepareStatement(
			"SELECT id FROM productDirs WHERE dir_path = ?"
		)
	}

	def prepareRetrieveDepId{
		retrieveDepIdStatement = connection.prepareStatement(
			"SELECT id FROM dependencies WHERE hash = ?"
		)
	}

	def prepareDeleteProductDir{
		deleteProductDirStatement = connection.prepareStatement(
			"DELETE FROM productDirs WHERE dir_path = ?"
		)
	}

	def prepareSelectProductDirs{
		selectProductDirsStatement = connection.prepareStatement(
			"Select dir_path FROM productDirs"
		)
	}
}

